/* ******************************************************
BreadboArduino_Hello_World Joe Pardue February 10, 2010
****************************************************** */
#include <stdio.h>
#include <avr/io.h>	// included in SmileyUSART.h
#include "SmileyUSART.h"

//#define FOSC 16000000
//#define BAUD 57600
//#define MYUBRR FOSC/16/BAUD-1

uint8_t receiveByte( void );
//void init();
void Initialization();

// From example in avrlibc manual
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = \
FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

const char YOUSENT[] PROGMEM = "This is a test string\n";  // added by RS for debugging

int main(void) 
{ 
    uint8_t b;
    uint8_t count = 0;

    //init();
	Initialization();

	sendStringPROGMEM(YOUSENT);

    printf("BBArduino_Hello_Test v 005\n");

    while(1)
    {
		b = receiveByte();

		printf("#%d",count++); 
		printf("You sent: %c\n",b); 
    }

    return 0;
}

static int uart_putchar(char c, FILE *stream)
{ 
    if (c == '\n') uart_putchar
    ('\r', stream);
    // wait for UDR to be clear
    //loop_until_bit_is_set(UCSR0A, UDRE0);
	//loop_until_bit_is_set(UCSRA, UDRE);
	loop_until_bit_is_set(USART_CONTROL_STATUS_REG_A, USART_READY_TO_TRANSMIT);  // added by RS for generalization
    //UDR0 = c;    //send the character
	//UDR = c;
	USART_DATA_REG = c;		// added by RS for generalization
    return 0; 
} 

// RS Removed the following and replaced with the code from YouSent.c that follows
/**********************************************
void init()
{
    //USART Baud rate: 57600
    UBRR0H = (MYUBRR) >> 8;
    UBRR0L = MYUBRR;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);    

    //set the output stream
    stdout = &mystdout;    
}

uint8_t receiveByte( void )
{
    // Wait for data to be received 
    while ( !(UCSR0A & (1<<RXC0)) );	
    // Get and return received data from buffer 
    return UDR0;
}
***********************************************/



/*****************************************************
	USART Functions
******************************************************/
void USARTInit()
{
	// Set baud rate hard coded to 19200 for 12MHz
	//USART_BAUD_RATE_LOW = 38;
	// TODO redo header to generalize baudrate calcuation

	// ***** TODO completed by RS 7/24/2014 ******

#if defined(Arduino) || defined(ATmega644)
	USART_BAUD_RATE_LOW =(uint8_t)(F_CPU/(BAUD_RATE*16)-1);

#elif defined(Butterfly)
	USART_BAUD_RATE_LOW =(uint8_t)(F_CPU/(BAUD_RATE*12)-1);

#endif
	// TODO is the HIGH really ever needed???
	//USART_BAUD_RATE_HIGH = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
	//USART_BAUD_RATE_LOW =(uint8_t)(F_CPU/(BAUD_RATE*16L)-1);

	// Enable receiver and transmitter
	USART_CONTROL_STATUS_REG_B = (1 << USART_ENABLE_RECEIVER) | (1 << USART_ENABLE_TRANSMITTER);
	// Set frame format: n,8,1
#if defined(ATmega644) || defined(Arduino)
	USART_CONTROL_STATUS_REG_C |= (1 << USART_STOP_BIT_SELECT) | (1 << USART_CHARACTER_SIZE);  
#elif defined(ATmega32)
	USART_CONTROL_STATUS_REG_C |= (1 << USART_REGISTER_SELECT) | (1 << USART_CHARACTER_SIZE_0) | (1 << USART_CHARACTER_SIZE_1);

#elif defined(Butterfly)
	// for the Butterfly reuse the Atmel code for the USART in the intialization process
#else
   #error "No USART definition for MCU in USARTInit"
#endif 
}

void sendByte( uint8_t data )
{
	wdt_reset(); // reset the watchdog timer, if it is set
	// Wait for empty transmit buffer 
	while ( !( USART_CONTROL_STATUS_REG_A & (1<<USART_READY_TO_TRANSMIT)) );
	// Put data into buffer, sends the data
	USART_DATA_REG = data;
}

uint8_t receiveByte( void )
{
	wdt_reset(); // reset the watchdog timer, if it is set
	// Wait for data to be received 
	while ( !(USART_CONTROL_STATUS_REG_A & (1<<USART_RECEIVE_COMPLETE)) );	
	// Get and return received data from buffer 
	return USART_DATA_REG;
}

void sendStringPROGMEM(const char *FlashString)
{
	int i = 0;

	// The 'for' logic terminates if the byte is '\0' or if i = 60.
	// '\0' is 'null' and terminates C strings
	// The 80 prevents too much overrun if we get a bad pointer
	// and it limits the string size	
	for( i = 0 ; pgm_read_byte(&FlashString[i]) && i < 80; i++) 
	{
			sendByte(pgm_read_byte(&FlashString[i]));
	}
}

/*****************************************************
	Initialization
******************************************************/

void Initialization()
{
	USARTInit();

	stdout = &mystdout; 

#if defined(Butterfly)
// NOTE this all copied directly from the original Butterfly source code
// so it uses a different method for naming registers than the rest of the code
// updated register names to fit new io headers - why do they keep changing this?

    OSCCAL_calibration();   // Calibrate the OSCCAL byte

    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
    DIDR1 = (7<<ADC0D);

    PORTB = 0xFF;       // Enable pullups on Ports B and E
    PORTE = 0xFF;
    
    // boost IntRC to 2Mhz to achieve 19200 baudrate
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 4, Inter RC 8Mhz / 4 = 2Mhz
    CLKPR = (1<<CLKPS1);    

    UBRRH = 0;//(unsigned char)(baudrate>>8);
    UBRRL = 12;//(unsigned char)baudrate;

    // Enable 2x speed
    UCSRA = (1<<U2X);

    // Enable receiver and transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<UDRIE);

    // Async. mode, 8N1
    UCSRC = (0<<UMSEL)|(0<<UPM1)|(0<<USBS)|(3<<UCSZ0)|(0<<UCPOL);
        
    MCUCR = (1<<IVCE);       
    MCUCR = (1<<IVSEL);             //move interruptvectors to the Boot sector    

    sei(); // mt __enable_interrupt();
    
    // mt PCMSK1 = (1<<PORTB6) | (1<<PORTB4);
	PCMSK1 = (1<<PINB6) | (1<<PINB4);       // set pin-change interrupt mask
  	EIFR   = (1<<PCIF1);                    // clear external intterupt flag 1
	EIMSK  = (1<<PCIE1);   

/*
    OSCCAL_calibration();   // Calibrate the OSCCAL byte

    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
    DIDR1 = (7<<ADC0D);

    PORTB = 0xFF;       // Enable pullups on Ports B and E
    PORTE = 0xFF;
    
    // boost IntRC to 2Mhz to achieve 19200 baudrate
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 4, Inter RC 8Mhz / 4 = 2Mhz
    CLKPR = (1<<CLKPS1);    

    UBRR0H = 0;//(unsigned char)(baudrate>>8);
    UBRR0L = 12;//(unsigned char)baudrate;

    // Enable 2x speed
    UCSR0A = (1<<U2X0);

    // Enable receiver and transmitter
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(0<<RXCIE0)|(0<<UDRIE0);

    // Async. mode, 8N1
    UCSR0C = (0<<UMSEL0)|(0<<UPM01)|(0<<USBS0)|(3<<UCSZ00)|(0<<UCPOL0);
        
    MCUCR = (1<<IVCE);       
    MCUCR = (1<<IVSEL);             //move interruptvectors to the Boot sector    

    sei(); // mt __enable_interrupt();
    
    // mt PCMSK1 = (1<<PORTB6) | (1<<PORTB4);
	PCMSK1 = (1<<PINB6) | (1<<PINB4);       // set pin-change interrupt mask
  	EIFR   = (1<<PCIF1);                    // clear external intterupt flag 1
	EIMSK  = (1<<PCIE1);   


*/

#endif
}


// Only compile if Butterfly is defined in SmileyBootloader.h
#if defined(Butterfly)
/*****************************************************
	Oscillator Calibration
	The following code is taken directly from Martin Thoma's
	port of Atmel's IAR Butterfly software to GCC
******************************************************/
/*****************************************************************************
*
*   Function name : OSCCAL_calibration
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Calibrate the internal OSCCAL byte, using the external 
*                   32,768 kHz crystal as reference
*
*****************************************************************************/
void OSCCAL_calibration(void)
{
    unsigned char calibrate = 0;//FALSE;
    int temp;
    unsigned char tempL;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
    
    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
    
    OCR2A = 200;            // set timer2 compare value 

    TIMSK0 = 0;             // delete any interrupt sources
        
    TCCR1B = (1<<CS10);     // start timer1 with no prescaling
    TCCR2A = (1<<CS20);     // start timer2 with no prescaling

    while((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2UB to be cleared

    Delay(1000);    // wait for external crystal to stabilise
    
    while(!calibrate)
    {
        cli(); // mt __disable_interrupt();  // disable global interrupt
        
        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags
        
        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter
           
	while ( !(TIFR2 & (1<<OCF2A)) ); // while ( !(TIFR2 && (1<<OCF2A)) );   // wait for timer2 compareflag
    
        TCCR1B = 0; // stop timer1

        sei(); // __enable_interrupt();  // enable global interrupt
    
        if ( (TIFR1 & (1<<TOV1)) ) // if ( (TIFR1 && (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }
    
        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs to fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs to slow, increase the OSCCAL
        }
        else
            calibrate = 1;//TRUE;   // the interRC is correct
    
        TCCR1B = (1<<CS10); // start timer1
    }
}


/*****************************************************************************
*
*   Function name : Delay
*
*   Returns :       None
*
*   Parameters :    unsigned int millisec
*
*   Purpose :       Delay-loop
*
*****************************************************************************/
void Delay(unsigned int millisec)
{
    unsigned char i; // mt int i;
    
    while (millisec--)
        for (i=0; i<125; i++);
}

#endif
